	/*
 * File:   FileSystem.h
 * Author: nahumba
 *
 * Created on July 7, 2009, 8:27 PM
 */

#ifndef _FILESYSTEM_H
#define	_FILESYSTEM_H
#include "Exception.h"
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestAssert.h>
#include "LowLevelLayer.h"
#include <vector>
using std::vector;
typedef char* buffer;
typedef unsigned int offset;
typedef unsigned int bytes;
//typedef int list;//TODO change list type
typedef struct FileHeader{
	INodeId iNodeNumber;
	char fileName[12];
};
//typedef std::vector<FileHeader_rec> list;
//class iNode;
class FileSystem {
public:
	FileSystem(string& fileName, unsigned int blockSize, unsigned int diskSize, unsigned int iNodesCount);
	~FileSystem();
	//    createFile(flag);
	INodeId createDir();
	INodeId createFile(INodeType);
	INodeType getFileType(INodeId);
	void f_read(INodeId,buffer,offset,bytes);
	void f_write(INodeId,buffer,offset,bytes);
	vector<FileHeader>*    d_read(INodeId);
	void    d_write(INodeId,std::vector<FileHeader>*);
	void f_delete(INodeId);
	void d_delete(INodeId);
	INodeId getRootINode();
private:
	LowLevelLayer _disk;
	void bufferToFileHeader(FileHeader& ,char* );
	void fileHeaderToBuffer(char*,FileHeader);
	void listToBuffer(vector<FileHeader>,char*,unsigned int );
	void f_readrec(INodeId inode,buffer buf,offset off,bytes nBytes);
	void f_writerec(INodeId inode,buffer buf,offset off,bytes nBytes);
	void fragmentBlockAllocation(INodeId inode,BlockId blockNumber);
};

class FileSystemTestCase : public CPPUNIT_NS::TestCase {
CPPUNIT_TEST_SUITE(FileSystemTestCase);
	CPPUNIT_TEST(newRootDir);
	CPPUNIT_TEST(createSlashDotFile);
	CPPUNIT_TEST_EXCEPTION(tooMuchFiles,Exception);
	CPPUNIT_TEST_EXCEPTION(tooMuchDirectories,Exception);
	CPPUNIT_TEST(getFileType);
	CPPUNIT_TEST(simpleDeleteDir);
	CPPUNIT_TEST(simpleDeleteFile);
	CPPUNIT_TEST(readDataFromFile);
	CPPUNIT_TEST(writeDataTest);
	CPPUNIT_TEST(bigReadFromFile);
	CPPUNIT_TEST(writeReadDataToFile);
	CPPUNIT_TEST(writeDirectroyData);
	CPPUNIT_TEST(readDirectroyData);
	CPPUNIT_TEST(simpleDemo);
	CPPUNIT_TEST_SUITE_END();

public:
	FileSystem* _TestingDisk;
	LowLevelLayer* _low;
//	INodeId _rootDir;
	FileHeader _slashdot;
	FileHeader _rootDir;
	void setUp(void) {
		cout<<"\nSET UP"<<endl;
		string st("FILE_SYS");
		_TestingDisk=new FileSystem(st,64,6800,10);
		newRootDir();
		createSlashDotFile();
	}
	void createSlashDotFile(){
		_slashdot.iNodeNumber =_TestingDisk->createFile(FILE_TYPE);
		strcpy(_slashdot.fileName , ".");
		cout<<"file name "<< _slashdot.fileName<<"inode : "<<_slashdot.iNodeNumber<<endl;
//		_TestingDisk->f_write(_slashdot.iNodeNumber,"abcd",0,4);
	}

	void newRootDir(void) {
		_rootDir.iNodeNumber = _TestingDisk->createDir();
		strcpy(_rootDir.fileName, "\\");
	}
	void tearDown(void) {
		delete _TestingDisk;

		remove("FILE_SYS");
		cout<<"\ntear down"<<endl;
	}

protected:
	void tooMuchFiles(void) {
			for (int i=0; i<1001; i++)
				_TestingDisk->createFile(FILE_TYPE);
	}
	void tooMuchDirectories(void) {
		for (int i=0; i<1001; i++)
			_TestingDisk->createDir();
	}
	void getFileType(void){
		CPPUNIT_ASSERT_EQUAL_MESSAGE("error, root folder should be directory type" ,DIRECTORY_TYPE  ,_TestingDisk->getFileType(_rootDir.iNodeNumber));
		CPPUNIT_ASSERT_EQUAL_MESSAGE("error, root folder should be File type" ,FILE_TYPE,_TestingDisk->getFileType(_slashdot.iNodeNumber));
	}
	void simpleDeleteDir(void){
		_TestingDisk->d_delete(_rootDir.iNodeNumber);
	}
	void simpleDeleteFile(void){
		_TestingDisk->d_delete(_slashdot.iNodeNumber);
	}
	/************************/
	void readDataFromFile(void){
		char fileData[10];
		strcpy(fileData,"abcd");
		cout<<"fileData before: "<<fileData<<endl;
		_TestingDisk->f_write(_slashdot.iNodeNumber,fileData,0,4);
		cout<<"fileData middle: "<<fileData<<endl;
		fileData[0]='\0';
		_TestingDisk->f_read(_slashdot.iNodeNumber,fileData,0,4);
		cout<<"fileData after: "<<fileData<<endl;
		CPPUNIT_ASSERT_EQUAL(strcmp(fileData,"abcd"),0);
	}
	void bigReadFromFile(void){
			char fileData[1024];
			char readBuff[1024];
			char input[1024];
			const unsigned int secondBlockPosition = 65;
//			for(int i=0;i<261;i++){
////				fileData[i]=numberToLetter(i);
//				input[i]=numberToLetter(i);
//			}
			for(int i=0;i<64;i++){
				input[i]='a';
				input[i+64]='b';
				input[i+128]='c';
			}
			cout<<"IIIIIIIIIIIIIIIII: input will be "<<input<<endl;
			_TestingDisk->f_write(_slashdot.iNodeNumber,input,0,128);
			_TestingDisk->f_read(_slashdot.iNodeNumber,fileData,secondBlockPosition ,8);
			_TestingDisk->f_read(_slashdot.iNodeNumber,readBuff,0,128);
			cout<<"OOOOOOOOOOOOO read buufer: "<< readBuff << endl;
			cout<<"fileData : "<< fileData << endl;

//				cout<<"BEFORE ASSERT : "<< fileData << endl;
			CPPUNIT_ASSERT_EQUAL(strncmp(fileData,"bb",2),0);
			CPPUNIT_ASSERT_EQUAL(strncmp(readBuff,"aaaaaaaa",8),0);
	}
	void f_writeTests(char* fileData){
		for(int i=0;i<200;i++){
			fileData[i]=numberToLetter(i);
		}
		_TestingDisk->f_write(_slashdot.iNodeNumber,fileData,0,200);
	}
	/*******************/
	void writeDataTest(){
		char a[1024];
		       f_writeTests(a);
	}
	void f_readTest(char* fileData){
		_TestingDisk->f_read(_slashdot.iNodeNumber,fileData,0,4);
	}
	char numberToLetter(int i){
		return (char)(97+(i%26));
	}
	void writeReadDataToFile(void){
		char fileData[1024];
		cout<<"frwite:"<<endl;
		f_writeTests(fileData);
		CPPUNIT_ASSERT_EQUAL(strncmp(fileData,"abcd",4),0);
		cout<<"fread:"<<endl;
		f_readTest(fileData);
		cout<<"end"<<endl;

		CPPUNIT_ASSERT_EQUAL(strncmp(fileData,"abcd",4),0);
	}
	void readDirectroyData(void){
		writeDirectroyData();
		vector<FileHeader>* readData = _TestingDisk->d_read(_rootDir.iNodeNumber);
		vector<FileHeader> data = *readData;
		FileHeader fh = data.front();
		cout<<fh.fileName<<endl;
		CPPUNIT_ASSERT_EQUAL(strcmp(fh.fileName,_slashdot.fileName),0);
		cout<<fh.iNodeNumber<<'\t'<<_slashdot.iNodeNumber<<endl;
		CPPUNIT_ASSERT_EQUAL(fh.iNodeNumber,_slashdot.iNodeNumber);
//		delete &readData;

	}
	void writeDirectroyData(void){
		vector<FileHeader>* data= new vector<FileHeader>();
		cout<<"size "<<data->size()<<endl;
		data->push_back(_slashdot);
		data->push_back(_slashdot);
		data->push_back(_slashdot);
		cout<<"size "<<data->size()<<endl;
		cout<<"slash dot node:"<<_slashdot.iNodeNumber<<endl;
		_TestingDisk->d_write(_rootDir.iNodeNumber,data);
	}
	void simpleDemo(void){
		newRootDir();
		createSlashDotFile();
		vector<FileHeader> root;
		FileHeader* fh = new FileHeader;
		strncpy(fh->fileName,_slashdot.fileName,12);
		fh->iNodeNumber = _slashdot.iNodeNumber;
		root.push_back(*fh);
		_TestingDisk->d_write(_rootDir.iNodeNumber,&root);
		vector<FileHeader>* res = _TestingDisk->d_read(_rootDir.iNodeNumber);
		cout<<"expected root: "<<fh->fileName<<"inode "<<fh->iNodeNumber<<endl;
		delete fh;
		for(vector<FileHeader>::iterator iter = res->begin();iter!=res->end();iter++)
			cout<<"actual root: "<<iter->fileName<<"inode "<<iter->iNodeNumber<<endl;
//		delete res;
 	}
	void writeFolder(){
	}
};
#endif	/* _FILESYSTEM_H */

